/**
 * Copyright (C) 2013 Open Whisper Systems
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.appspot.eusms.service;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

import com.appspot.eusms.database.DatabaseFactory;
import com.appspot.eusms.database.MmsDatabase;
import com.appspot.eusms.database.ThreadDatabase;
import com.appspot.eusms.mms.MmsSendResult;
import com.appspot.eusms.notifications.MessageNotifier;
import com.appspot.eusms.recipients.Recipients;
import com.appspot.eusms.sms.IncomingIdentityUpdateMessage;
import com.appspot.eusms.transport.InsecureFallbackApprovalException;
import com.appspot.eusms.transport.RetryLaterException;
import com.appspot.eusms.transport.SecureFallbackApprovalException;
import com.appspot.eusms.transport.UndeliverableMessageException;
import com.appspot.eusms.transport.UniversalTransport;
import com.appspot.eusms.transport.UntrustedIdentityException;

import com.appspot.eusms.R;
import com.appspot.eusms.service.SendReceiveService.ToastHandler;

import org.whispersystems.textsecure.crypto.MasterSecret;

import ws.com.google.android.mms.MmsException;
import ws.com.google.android.mms.pdu.SendReq;

public class MmsSender {

    private final Context context;
    private final SystemStateListener systemStateListener;
    private final ToastHandler toastHandler;

    public MmsSender(Context context, SystemStateListener systemStateListener, ToastHandler toastHandler) {
        this.context = context;
        this.systemStateListener = systemStateListener;
        this.toastHandler = toastHandler;
    }

    public void process(MasterSecret masterSecret, Intent intent) {
        Log.w("MmsSender", "Got intent action: " + intent.getAction());
        if (SendReceiveService.SEND_MMS_ACTION.equals(intent.getAction())) {
            handleSendMms(masterSecret, intent);
        }
    }

    private void handleSendMms(MasterSecret masterSecret, Intent intent) {
        long messageId = intent.getLongExtra("message_id", -1);
        MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
        ThreadDatabase threads = DatabaseFactory.getThreadDatabase(context);
        UniversalTransport transport = new UniversalTransport(context, masterSecret);

        try {
            SendReq[] messages = database.getOutgoingMessages(masterSecret, messageId);

            for (SendReq message : messages) {
                long threadId = database.getThreadIdForMessage(message.getDatabaseMessageId());

                try {
                    Log.w("MmsSender", "Passing to MMS transport: " + message.getDatabaseMessageId());
                    database.markAsSending(message.getDatabaseMessageId());
                    MmsSendResult result = transport.deliver(message, threadId);

                    if (result.isUpgradedSecure())
                        database.markAsSecure(message.getDatabaseMessageId());
                    if (result.isPush()) database.markAsPush(message.getDatabaseMessageId());

                    database.markAsSent(message.getDatabaseMessageId(), result.getMessageId(),
                            result.getResponseStatus());

                    systemStateListener.unregisterForConnectivityChange();
                } catch (InsecureFallbackApprovalException ifae) {
                    Log.w("MmsSender", ifae);
                    database.markAsPendingInsecureSmsFallback(message.getDatabaseMessageId());
                    notifyMessageDeliveryFailed(context, threads, threadId);
                } catch (SecureFallbackApprovalException sfae) {
                    Log.w("MmsSender", sfae);
                    database.markAsPendingSecureSmsFallback(message.getDatabaseMessageId());
                    notifyMessageDeliveryFailed(context, threads, threadId);
                } catch (UndeliverableMessageException e) {
                    Log.w("MmsSender", e);
                    database.markAsSentFailed(message.getDatabaseMessageId());
                    notifyMessageDeliveryFailed(context, threads, threadId);
                } catch (UntrustedIdentityException uie) {
                    IncomingIdentityUpdateMessage identityUpdateMessage = IncomingIdentityUpdateMessage.createFor(message.getTo()[0].getString(), uie.getIdentityKey());
                    DatabaseFactory.getEncryptingSmsDatabase(context).insertMessageInbox(masterSecret, identityUpdateMessage);
                    database.markAsSentFailed(messageId);
                } catch (RetryLaterException e) {
                    Log.w("MmsSender", e);
                    database.markAsOutbox(message.getDatabaseMessageId());

                    if (systemStateListener.isConnected()) scheduleQuickRetryAlarm();
                    else systemStateListener.registerForConnectivityChange();

                    toastHandler
                            .obtainMessage(0, context.getString(R.string.SmsReceiver_currently_unable_to_send_your_sms_message))
                            .sendToTarget();
                }
            }
        } catch (MmsException e) {
            Log.w("MmsSender", e);
            if (messageId != -1)
                database.markAsSentFailed(messageId);
        }
    }

    private static void notifyMessageDeliveryFailed(Context context, ThreadDatabase threads, long threadId) {
        Recipients recipients = threads.getRecipientsForThreadId(threadId);
        MessageNotifier.notifyMessageDeliveryFailed(context, recipients, threadId);
    }

    private void scheduleQuickRetryAlarm() {
        ((AlarmManager) context.getSystemService(Context.ALARM_SERVICE))
                .set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + (30 * 1000),
                        PendingIntent.getService(context, 0,
                                new Intent(SendReceiveService.SEND_MMS_ACTION,
                                        null, context, SendReceiveService.class),
                                PendingIntent.FLAG_UPDATE_CURRENT
                        )
                );
    }
}
